
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Activiti工作流课程 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>Activiti工作流课程 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">什么是工作流管理系统（WfMS）的目标？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">工作流管理系统（WfMS）的目标是：管理工作的流程以确保工作在正确的时间被期望的人员所执行——在自动化进行的业务过程中插入人工的执行和干预。</div>
          </div>
          <div class="card-source">来源: 1：工作流的概念</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">Activiti数据库表的命名规则中，`ACT_RE_*`, `ACT_RU_*`, 和 `ACT_HI_*` 分别代表什么用途？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`ACT_RE_*`: 'RE'表示repository（仓库），包含流程定义和流程静态资源。`ACT_RU_*`: 'RU'表示runtime（运行时），包含流程实例、任务等运行中的数据，流程结束时会删除这些记录。`ACT_HI_*`: 'HI'表示history（历史），包含历史流程实例、变量、任务等历史数据。</div>
          </div>
          <div class="card-source">来源: 3.3：数据库（先学后看）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">API</div>
          <div class="card-question">Activiti的核心API中，`RepositoryService`, `RuntimeService`, 和 `TaskService` 的主要职责分别是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">API</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`RepositoryService`负责管理流程定义；`RuntimeService`负责执行管理，包括启动、推进、删除流程实例等操作；`TaskService`负责任务管理。</div>
          </div>
          <div class="card-source">来源: 5.1：ProcessEngine</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">概念</div>
          <div class="card-question">在Activiti中，`ProcessInstance`（流程实例）和`Execution`（执行对象）有什么区别和联系？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">概念</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">ProcessInstance代表一个流程从开始到结束的最大流程分支，一个流程实例只有一个。Execution描述流程执行的每一个节点。在没有并发的单线流程中，ProcessInstance与Execution是一致的；在有并发（如并行网关）的流程中，总线路代表ProcessInstance，而每个并发的分支线路代表一个Execution。总结来说，一个流程中，执行对象可以存在多个，但流程实例只能有一个。</div>
          </div>
          <div class="card-source">来源: 5.8：Execution</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">当部署一个新的流程定义时，主要会涉及Activiti数据库中的哪三张表？它们各自存储什么信息？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">主要涉及三张表：1) `act_re_deployment`（部署对象表），存放流程定义的显示名和部署时间。2) `act_re_procdef`（流程定义表），存放流程定义的属性信息。3) `act_ge_bytearray`（资源文件表），存储流程定义相关的部署信息，如bpmn规则文件和图片，以二进制形式存储。</div>
          </div>
          <div class="card-source">来源: 7.2：部署流程定义（classpath路径加载文件）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">什么是流程变量？当流程结束后，流程变量会如何处理？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">流程变量是在整个工作流中用于设置和获取业务参数的变量，其作用域对应一个流程实例。当流程实例结束完成后，流程变量不会被删除，而是保存在数据库的历史表中（`act_hi_varinst`）。</div>
          </div>
          <div class="card-source">来源: 9：流程变量</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">若要将一个JavaBean对象用作流程变量，该JavaBean类必须满足什么条件？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">该JavaBean类必须实现 `Serializable` 接口。此外，为避免因类属性变化导致的反序列化异常，建议固定其序列化ID（serialVersionUID）。</div>
          </div>
          <div class="card-source">来源: 9.4：设置流程变量</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在Activiti中，`setVariable` 和 `setVariableLocal` 在设置流程变量时有什么核心区别？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`setVariable` 设置的是全局流程变量，在整个流程实例中都可见，当名称相同时，后一次的值会覆盖前一次。`setVariableLocal` 设置的是本地流程变量，仅在当前活动的节点（任务）范围内有效，当流程继续到下一个任务时将无法获取。即使不同任务设置了同名的本地变量，它们也不会相互覆盖。</div>
          </div>
          <div class="card-source">来源: 9.9：总结</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">并行网关（Parallel Gateway）有什么特性？它会解析顺序流上定义的条件吗？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">并行网关具有分支（fork）和汇聚（join）的功能。分支时为每个外出的顺序流都创建一个并发分支；汇聚时会等待所有进入的并发分支都到达后，流程才会通过汇聚网关继续。并行网关不会解析条件，即使顺序流中定义了条件，也会被忽略。</div>
          </div>
          <div class="card-source">来源: 13：并行网关(parallelGateWay)</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">什么是接收活动（Receive Task）？如何触发它以使流程继续执行？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">接收活动（Receive Task）是一个等待任务，当流程执行到这里时会进入等待状态，直到引擎接收到一个特定的消息。可以通过调用 `runtimeService.signal(executionId)` 方法给流程引擎发送信号，这会触发流程穿过接收任务继续执行。</div>
          </div>
          <div class="card-source">来源: 15：接收活动（receiveTask，即等待活动）</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在Activiti中，分配个人任务（User Task）有哪三种主要方式？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">1. 在BPMN流程定义文件中直接通过 `assignee` 属性写定办理人。2. 使用流程变量，在 `assignee` 属性中写入表达式 `#{userID}`，然后在启动流程或完成上一步任务时设置该变量的值。3. 使用 `TaskListener` 监听器接口，在实现类中通过 `delegateTask.setAssignee()` 方法动态指定办理人。</div>
          </div>
          <div class="card-source">来源: 16.1.5：总结</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在Activiti的组任务中，“认领任务”（Claim Task）指的是什么操作？如何将会认领后的任务退还给组？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">“认领任务”是指将一个组任务分配给一个具体的个人来办理，使其从候选任务变为该用户的个人任务，这通过 `taskService.claim(taskId, userId)` 实现。要将已认领的任务退还给组任务（前提是它之前是一个组任务），可以调用 `taskService.setAssignee(taskId, null)`，将任务的办理人设置为空。</div>
          </div>
          <div class="card-source">来源: 16.2.5：总结</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
